/*
 * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.mybatisplus.extension.plugins.inner;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.AbstractJoinWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 配合join相关wrapper 动态修改resultmap
 * @author wanglei
 * @since 2022-04-26
 */
public class JoinInterceptor implements InnerInterceptor {

    /**
     * 缓存MappedStatement,不需要每次都去重写构建MappedStatement
     */
    private static final Map<String, Map<Configuration, MappedStatement>> MS_CACHE = new ConcurrentHashMap<>();


    @Override
    public MappedStatement changeMappedStatement(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (parameter != null && parameter instanceof Map) {
            if (((Map) parameter).containsKey("ew")) {
                Object ew = ((Map) parameter).get("ew");
                if (ew != null && ew instanceof AbstractJoinWrapper && ms.getResultMaps() != null && !ms.getResultMaps().isEmpty()) {
                    return this.newMappedStatement(ms, ms.getResultMaps().get(0).getType());
                }
            }
        }
        return ms;
    }


    /**
     * 构建新的MappedStatement
     * from mpj
     */
    public MappedStatement newMappedStatement(MappedStatement ms, Class<?> resultType) {
        if (!resultType.isAnnotationPresent(TableName.class)) {
            return ms;
        } else {
            String id = ms.getId() + "_" + resultType.getName();
            Map<org.apache.ibatis.session.Configuration, MappedStatement> statementMap = MS_CACHE.get(id);
            if (CollectionUtils.isNotEmpty(statementMap)) {
                MappedStatement statement = statementMap.get(ms.getConfiguration());
                if (Objects.nonNull(statement)) {
                    return statement;
                }
            }

            MappedStatement.Builder builder = (new MappedStatement.Builder(ms.getConfiguration(), id, ms.getSqlSource(), ms.getSqlCommandType())).resource(ms.getResource()).fetchSize(ms.getFetchSize()).statementType(ms.getStatementType()).keyGenerator(ms.getKeyGenerator()).timeout(ms.getTimeout()).parameterMap(ms.getParameterMap()).resultSetType(ms.getResultSetType()).cache(ms.getCache()).flushCacheRequired(ms.isFlushCacheRequired()).useCache(ms.isUseCache());
            if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
                builder.keyProperty(String.join(",", ms.getKeyProperties()));
            }

            List<ResultMap> resultMaps = new ArrayList<>();
            String joinResultMap = TableInfoHelper.getTableInfo(resultType).getResultMapForJoin();
            if (joinResultMap == null) {
                return ms;
            }
            resultMaps.add(ms.getConfiguration().getResultMap(TableInfoHelper.getTableInfo(resultType).getResultMapForJoin()));
            builder.resultMaps(resultMaps);
            MappedStatement mappedStatement = builder.build();
            if (statementMap == null) {
                statementMap = new ConcurrentHashMap<>();
                MS_CACHE.put(id, statementMap);
            }

            statementMap.put(ms.getConfiguration(), mappedStatement);
            return mappedStatement;
        }
    }
}
